#include "udpBase.h"

const qint64 kMaxBufLen = 4 * 1024 * 1024; /* 4MB */

CUdpBase::CUdpBase(QObject *parent) : QObject(parent),
	m_thRecv(new QThread()),
	m_recvSock(new QUdpSocket()),
	m_sendSock(new QUdpSocket())
{
    m_readBuf.resize(kMaxBufLen);
	m_recvSock->moveToThread(m_thRecv);
	m_sendSock->moveToThread(m_thRecv);
	this->moveToThread(m_thRecv);
    m_thRecv->start();

    connect(m_recvSock, SIGNAL(readyRead()),
            this, SLOT(slot_dataReceived()), Qt::DirectConnection);

    connect(this, &CUdpBase::sigWrite, this, &CUdpBase::slotWrite);
    connect(this, &CUdpBase::sigDisConn, this, &CUdpBase::slotDisConn);
	connect(this, &CUdpBase::sigBind, this, &CUdpBase::slotBind);

    m_isConn = false;
}

CUdpBase::~CUdpBase()
{
    DisConn();

    delete m_sendSock;
    m_sendSock = 0;
    delete m_recvSock;
    m_recvSock = 0;


}

/* 绑定本地端口 */
bool CUdpBase::BindLocalPort(qint16 localBind)
{
	m_lockBind.lock();
	const int nAvlb = m_semBind.available();
	if(nAvlb > 0)
	{
		m_semBind.tryAcquire(nAvlb);
	}
	emit sigBind(localBind);
	const bool bWait = m_semBind.tryAcquire(1, 5000);
	m_lockBind.unlock();

	return bWait ? m_isConn : false;
}

/* 数据接收槽函数 */
void CUdpBase::slot_dataReceived()
{
	/* 循环确认是否还有数据 */
	while(m_recvSock->hasPendingDatagrams())
	{
		/* 获取收到的数据长度 */
		qint64 rLen = m_recvSock->pendingDatagramSize();
        m_readBuf.resize(rLen);

		/* 接收数据 */
		m_recvSock->readDatagram(m_readBuf.data(), rLen);

		/* 发出信号 */
		emit sigDataReady(m_readBuf);

		/* 2020-12-10：两次接收之间加1ms延时，确保能收到所有数据 */
		QThread::msleep(1);
    }
}

qint64 CUdpBase::Write(const char* data,
                       qint64 maxSize,
                       QString strIp,
                       qint16 port )
{
    if(!m_isConn) return -1;
    m_lockWrite.lock();
    const int nAvail = m_semWrite.available();
    if(nAvail > 0)
    {
        m_semWrite.tryAcquire(nAvail);
    }
    emit sigWrite(data, maxSize, strIp, port);
    const bool bWait = m_semWrite.tryAcquire(1, 5000);
    m_lockWrite.unlock();
    QMutexLocker lk(&m_lockWriteLen);
    qint64 iRet = bWait ? m_iLen : 0;
    return iRet;
}
void CUdpBase::slotWrite(const char *pch,
                         qint64 maxSize,
                         QString sendIp,
                         qint16 sendPort)
{
    m_lockWriteLen.lock();
    m_iLen = -1;
    m_iLen = (maxSize < 0)?
                    m_sendSock->writeDatagram(pch, QHostAddress(sendIp),
                                              sendPort):
                    m_sendSock->writeDatagram(pch, maxSize,
										   QHostAddress(sendIp),sendPort);
    m_lockWriteLen.unlock();
    m_semWrite.release();
}

void CUdpBase::DisConn()
{
    m_lockDisConn.lock();
    const int nAvail = m_semDisConn.available();
    if(nAvail > 0)
    {
        m_semDisConn.tryAcquire(nAvail);
    }
    emit sigDisConn();
    m_semDisConn.tryAcquire(1, 5000);
	m_lockDisConn.unlock();
}

bool CUdpBase::isConn()
{
	return m_isConn;
}

void CUdpBase::slotDisConn()
{
    m_recvSock->abort();
	m_semDisConn.release();
}

void CUdpBase::slotBind(qint16 localBind)
{
	//绑定本地端口
	bool result= m_recvSock->bind(QHostAddress::AnyIPv4, localBind);

	if(!result)
	{
		m_isConn = false;
		m_semBind.release();
		return;
	}

	m_isConn = true;
	m_semBind.release();
}
